/*
 * TimeBlock.java
 * 
 * Copyright (c) 2008, Marshall Scorcio, Ralph Holzmann, 
 *                     Stewart Sweet, Tyler Hoersch, Jeffery Van Handel
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of Wisconsin Oshkosh nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

package main;
import java.text.DecimalFormat;

/**
 * This TimeBlock class was taken form 
 * http://code.google.com/p/verdandi/source
 * /browse/trunk/src/TimeBlock.java?spec=svn118&r=118
 * Liviu Patrasco fixed some bugs and implemented 
 * overlap(TimeBlock) method.
 * All changes are commented on the line they were made. 
 */
public class TimeBlock implements
java.io.Serializable
{ 
	private static final long serialVersionUID = 1L;
	private static final int BRAKE_BTW_CLASSES = 0;
	private static final int MIN_IN_HR = 60;
	private int startHour;
	private int startMinute;
	private int endHour;
	private int endMinute;
	private String my_weekdays;
	private boolean my_timeIsSet;
	
	/**
	 * Constructs a TimeBlock.
	 */
	public TimeBlock(final int startHour, final int startMinute, 
		final int endHour, final int endMinute, final String days)
	{
		this.startHour = startHour;
		this.startMinute = startMinute;
		this.endHour = endHour;
		this.endMinute = endMinute;
		my_weekdays = days;
	}
	
	/**
	 * Constructs a TimeBlock.
	 */
	public TimeBlock(final String the_startTime, final String the_endTime, final String the_days)
	{
		my_weekdays = the_days.trim();
		if (the_startTime.trim().equals("TBA") || the_endTime.trim().equals("TBA") || 
				the_days.trim().equals("TBA")) {
			my_timeIsSet = false;
			startHour = 0;
			endHour = 0;
			startMinute = 0;
			endMinute = 0;
			my_weekdays = "TBA";
		}
		else parseTime(the_startTime, the_endTime);
	}
	
	/**
	 * Helper method for constructor to parse string time.
	 */
	private void parseTime(final String the_startTime, final String the_endTime) {
	  
	  /* Added by Michael 5-29-11 
      if (the_startTime.trim().equals("TBA")){
        startHour = 00;
        startMinute = 00;
        endHour = 00;
        endMinute = 01;
        
      }
      else{        */
      
		if (the_startTime.trim().length() == 3) {
			startHour = Integer.parseInt(the_startTime.trim().substring(0, 1));
			startMinute = Integer.parseInt(the_startTime.trim().substring(2));
		}
		else {
			startHour = Integer.parseInt(the_startTime.trim().substring(0, 2));
			startMinute = Integer.parseInt(the_startTime.trim().substring(3));
		}

		if (the_endTime.trim().length() == 3) {
			endHour = Integer.parseInt(the_endTime.trim().substring(0, 1));
			endMinute = Integer.parseInt(the_endTime.trim().substring(2));
		}
		else {
			endHour = Integer.parseInt(the_endTime.trim().substring(0, 2));
			endMinute = Integer.parseInt(the_endTime.trim().substring(3));
		}
      //}//End Michael's else wrapper
      setMytimeIsSet(true);
      return;
	}
	
	public int getStartHour()
	{
		return startHour;
	}

	public int getStartMinute()
	{
		return startMinute;
	}

	public int getEndHour()
	{
		return endHour;
	}

	public int getEndMinute()
	{
		return endMinute;
	}

	/**
	 * Method added by Liviu Patrasco.
	 * Checks if two time blocks overlap.
	 * Overlapping occurs when the starting or ending 
	 * (or both) time of one time block is within
	 * the other time block. the other start <= start 
	 * and/or end time <= the other end, and at least one
	 * day of week coincides. 
	 * Preconditions: myTimeIsSet(), the_other != null, 
	 * start time of the_other is <= end time of the_other, 
	 * getWeekDays().length > 0
	 * Postconditions: both time blocks are unchanged.
	 * @param the_other the other time block.
	 * @return true if time blocks overlap, 
	 * and false otherwise.
	 */
	public boolean overlap(final TimeBlock the_other) {
		int startTime = startHour * MIN_IN_HR + 
				startMinute + BRAKE_BTW_CLASSES;
		int endTime = endHour * MIN_IN_HR + endMinute + 
									BRAKE_BTW_CLASSES;
		int otherStartTime = the_other.getStartHour() * 
					MIN_IN_HR + the_other.getStartMinute();
		int otherEndTime = the_other.getEndHour() * 
						MIN_IN_HR + the_other.getEndMinute();
		boolean shareDay = false;
		for (int i = 0; i < my_weekdays.length(); i++) {
			for (int j = 0; j < the_other.getWeekDays().length(); j++) {
				if (my_weekdays.charAt(i) == the_other.getWeekDays().charAt(j)) {
					shareDay = true;
				}
			}
		}
		return shareDay && 
		((otherStartTime >= startTime && otherStartTime <= endTime) || 
		   (otherEndTime >= startTime && otherEndTime <= endTime) ||
		   (startTime >= otherStartTime && startTime <= otherEndTime) ||
		   (endTime >= otherStartTime && endTime <= otherEndTime));
	}

	/**Method added by Liviu Patrasco.
	 * Tests if this timeBlock is inside of another (or equal). 
	 * Preconditions: myTimeIsSet(), the_other is not null, 
	 * start time is earlier than end time for both - 
	 * this and the_other timeblocks. Weekdays must 
	 * contain week days in their natural order.
	 * Postconditions: Timeblocks do not change.
	 * @param the_other the other time block.
	 * @return true if starting time and ending 
	 * time of this time block
	 * is <= the_other ending time and >= the_other starting time.
	 */
	public boolean isWithin(final TimeBlock the_other) {
		if (this.equals(the_other)) return true;
		int startTime = startHour * MIN_IN_HR + startMinute;
		int endTime = endHour * MIN_IN_HR + endMinute;
		int otherStartTime = the_other.getStartHour() * 
					MIN_IN_HR + the_other.getStartMinute();
		int otherEndTime = the_other.getEndHour() * 
					MIN_IN_HR + the_other.getEndMinute();
		char[] days = my_weekdays.toCharArray();
		boolean within = true;
		for (char day : days) {
		  if (the_other.getWeekDays().indexOf(day) < 0) {
		    within = false;
		  }
		}
		return startTime >= otherStartTime && 
		endTime <= otherEndTime && 
		within;
	}
	
	/**
	 * Sets the start time of the timeblock
	 * @param hour
	 * @param minute
	 */
	public void setStartTime(int hour, int minute) 
	{
		startHour = hour;
		startMinute = minute;
	}

	/**
	 * Sets the end time of the timeblock
	 * @param hour
	 * @param minute
	 */
	public void setEndTime(int hour, int minute)
	{
		endHour = hour;
		endMinute = minute;
	}

	/**
	 * Sets the days of the week as an array of days.
	 * Preconditions: the_days is not null, 
	 * the_days.size() is <= 7
	 * Postconditions: getDays() == the_days
	 */
	public void setWeekDays(String the_days) {
		my_weekdays = the_days;
	}

	/**
	 * A getter for list of Weekdays enums.
	 * @return a list of Weekdays enums.
	 */
	public String getWeekDays() {
		return my_weekdays;
	}
	
	/**
	 * Sets myTimeIsSet to true if time was set after this object's construction.
	 */
	public void setMytimeIsSet(boolean my_timeIsSet) {
		this.my_timeIsSet = my_timeIsSet;
	}
	
	public boolean equals(Object the_other) {
		boolean result = false;
	    
	    if (the_other != null &&
	        the_other.getClass() == getClass())
	    {
	    	TimeBlock other = (TimeBlock) the_other;
	      result = startHour == other.getEndHour() &&
	      		   endHour == other.getEndHour() &&
	      		   startMinute == other.getStartMinute() &&
	      		   endMinute == other.getEndMinute() &&
	      		   my_weekdays == other.getWeekDays();
	    }
	    
	    return result;
	}
	
	/**
	 * Overwritten HashCode method.
	 */
	public int hashCode() {
		return startHour + startMinute + endHour + endMinute;
	}
	
	/**
	 * @return TRUE if time is set, and FALSE if TBA.
	 */
	public boolean TimeIsSet() {
		return my_timeIsSet;
	}
	
	/**
     * {@inheritDoc}
     */
	@Override
	public String toString()
	{
		if (!my_timeIsSet) {
			return "TBA";
		}
		DecimalFormat decimalFormat = new DecimalFormat("00");

		String formatted = decimalFormat.format(getStartHour()) + 
		"" + decimalFormat.format(getStartMinute());
		formatted += "-" + decimalFormat.format(getEndHour()) + 
		"" + decimalFormat.format(getEndMinute());

		return formatted;
	}

}
